package core;

import game.player.Player;
import gui.GameButtonListener;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferStrategy;

import javax.swing.JFrame;

import utils.DBG;
import utils.Properties;
import core.gamestates.GameOverState;
import core.gamestates.InGameState;
import core.gamestates.MenuState;
import core.rendering.ScreenManager;

/**
 * This class holds the main game Loop, and propagates that to the gamestate
 * which manage all stuff involved in them.
 * 
 * @author Norvin Altamirano Ruiz.
 * 
 */
public class GameFlow extends JFrame implements Runnable, KeyListener,
		MouseMotionListener, MouseListener, GameButtonListener {

	public static final int EXIT_APLICATION = 0;
	public static final int SWITCH_TO_MENU_STATE = 1;
	public static final int SWITCH_TO_GAME_STATE = 2;
	public static final int SWITCH_TO_GAME_OVER_STATE = 3;

	/**
	 * This contains the changes between states.
	 */
	GameState current = null;
	GameState game = null;
	GameState menu = null;
	GameOverState gameOver = null;

	private static GameFlow instance;
	private Graphics2D dbg;
	private Robot robot;
    
	
	private RenderingHints renderHints;
	BufferStrategy bstr; // the buffer strategy to be used in render loop.

	private GameFlow(GraphicsConfiguration gconf) {
		super(gconf);
		renderHints= new RenderingHints(RenderingHints.KEY_ANTIALIASING,
		                          RenderingHints.VALUE_ANTIALIAS_OFF);
		renderHints.add(new RenderingHints(RenderingHints.KEY_RENDERING , RenderingHints.VALUE_RENDER_QUALITY));
	}

	public void run() {
		while (true) {
			try {
				bstr = this.getBufferStrategy();
				
				dbg = (Graphics2D) bstr.getDrawGraphics();
				dbg.setRenderingHints(renderHints);
				current.renderer.g2d = dbg;
				dbg.setColor(Color.white);
				dbg.fillRect(0, 0, Properties.SCREEN_WIDTH,
						Properties.SCREEN_HEIGHT + 30);

				current.render();
				current.update();

				dbg.dispose();
				bstr.show();

				Thread.sleep(10);

			} catch (InterruptedException e) {
				DBG.showMsg("Error en el loop. " + e, this);
				break;
			}
		}
	}

	public void init() {
		DBG.showMsg("Creado el GameFlow", this);
		// window init.
		menu = new MenuState();
		game = new InGameState();
		gameOver = new GameOverState((InGameState) game);
		current = menu;
		try {
			robot = new Robot();
		} catch (Exception e) {
			DBG.showMsg("Error en el robot", this);
		}

	}

	/**
	 * Returns the instance for gameFlowControl.
	 * 
	 * @return
	 */
	private static GameFlow createGameFlow() {

		GraphicsEnvironment env = GraphicsEnvironment
				.getLocalGraphicsEnvironment();

		GraphicsDevice device = env.getDefaultScreenDevice();
		GraphicsConfiguration gc = device.getDefaultConfiguration();

		instance = new GameFlow(gc);
		instance.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		instance.setSize(new Dimension(Properties.SCREEN_WIDTH,
				Properties.SCREEN_HEIGHT));

		/**
		 * ADDS the listeners to the game.
		 */
		instance.addKeyListener(instance);
		instance.addMouseListener(instance);
		instance.addMouseMotionListener(instance);

		if (!Properties.windowed) {
			instance.setUndecorated(true);
			ScreenManager.goToFullMode(instance);
		}

		instance.setVisible(true);
		instance.setResizable(false);
		/** Very important to create the buffer strategy for 2 page flip. * */
		instance.createBufferStrategy(2);
		instance.setLocationRelativeTo(null);
		return instance;
	}

	/**
	 * returns instance
	 * 
	 * @return
	 */
	public static GameFlow getInstance() {
		if (instance == null)
			createGameFlow();
		return instance;
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub
		if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
			if (current instanceof MenuState) {
				System.exit(0);
			} else {
				current.pause();
				current = menu;
			}
		}
		if (e.getKeyCode() == KeyEvent.VK_ENTER) {
			if (current instanceof MenuState) {
				current.pause();
				current = game;
			} else {

			}
		}
		current.onKeyPresed(e);
	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseDragged(MouseEvent e) {
		// TODO Auto-generated method stub
		// current.onClick(e);
	}

	@Override
	public void mouseMoved(MouseEvent e) {
		// TODO Auto-generated method stub
		if (current != null) {
			Point p = e.getPoint();
			
			current.updateMouse(p);
		} else {
			DBG.showMsg("current is null", this);
		}
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub
		// current.onClick(e);
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub
		current.onClick(e);
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub

	}

	public boolean doAction(int actionCode) {
		boolean retorn = false;
		switch (actionCode) {
		case EXIT_APLICATION:
			DBG.showMsg("exiting aplication, thanks for comming.", this);
			System.exit(0);
			break;
		case SWITCH_TO_MENU_STATE:
			DBG.showMsg("switching to menu", this);
			if (!(current instanceof MenuState)) {
				DBG.showMsg("switching to menu", this);
				current.pause();
				current = menu;
				retorn = true;
			}
			break;
		case SWITCH_TO_GAME_STATE:
			DBG.showMsg("switching to game state", this);
			if (!(current instanceof InGameState)) {
				current.pause();
				current = game;
				retorn = true;
			}
			break;

		case SWITCH_TO_GAME_OVER_STATE:
			DBG.showMsg("switching to Game Over state", this);
			if (!(current instanceof GameOverState)) {
				current.pause();
				current = null;
				game = new InGameState();
				Player.getInstance().reset();
				current = gameOver;
				retorn = true;
			}

		}
		return retorn;
	}

}
